/**
 * -------------------------------------------------------------
 * 增强文件上传, 可直接上传至阿里玩兔服务器
 * 如果不支持h5, 则通过后台服务器中转(需要服务器支持)
 * -------------------------------------------------------------
 * <form>
 * <img data-zupload="true" src="file://xxx.png"/>
 * <input type="file" data-zupload="true" name="pictureFile" /> <!-- 上传文件 -->
 * </form>
 * -------------------------------------------------------------
 * 配置data-zupload="true", 就会成为需要上传的文件, 只支持img和input type="file"
 * 如果配置data-zupload="xxx", 上传成功后的会根据xxx查找input来保存上传后的url; 如果找不到, 将会自动生成input[name=xxx]
 * -------------------------------------------------------------
 * $.fn.zupload(token, done);
 * $.fn.zupload(options);
 * -------------------------------------------------------------
 * @param options = {
 *		token:url|base64string|json, // 授权凭证, 如果是url则发送请求获取授权凭证; json = { ak,sk,namespace,dir,name,... }, 计算token的参数
 *		process:function(e), 进度回调函数, e = { element:DOMElement, index:int, count:int, percent:int }
 *		step:function(e), 每个文件上传成功的回调函数, e = { element:DOMElement, index:int, count:int, url:string }
 *		done:function(e), 全部完成后的回调函数; 如果没有需要上传的内容,或不支持直接上传, 则直接回调; e = { finish:int, count:int }
 *		error:function(e), 失败回调函数; return true则失败后仍然继续上传; e = { element:DOMElement, index:int, count:int, message:string }
 * }
 * count = 一共有几个文件需要上传
 * index = 当前上传第几个文件, 从0开始
 * finish = 完成了几个文件
 * percent = 当前文件上传进度, 0~100
 * step e.url = 文件上传成功后生成的url
 * error e.message = 失败原因
 * -------------------------------------------------------------
 * 注意: 
 * 1. 如果需要计算token, 则需要用到CryptoJS的BASE64, HMAC, SHA1
 *	<script src="assets/libs/crypto-js/core.js"></script>
 *	<script src="assets/libs/crypto-js/enc-base64.js"></script>
 *	<script src="assets/libs/crypto-js/hmac.js"></script>
 *	<script src="assets/libs/crypto-js/sha1.js"></script>
 * 2. img跨域问题
 * <img data-zupload="true" src="http://..." />, 会存在跨域的问题
 * 需要服务器允许且在img上加上crossOrigin才行, <img crossOrigin="*" src="..."/>
 * -------------------------------------------------------------
 * @author 赵卉华
 * date: 2016-11-13
 * -------------------------------------------------------------
 */
+function($) {
	var KEY = "__file_upload__";
	var INPUT = "data-zupload";
	$.fn.zupload = function(options, done) {
		if ($.dt.isString(options)) { options = { token:options }; }
		if (done && !options.done) { options.done = done; }

		return this.each(function() {
			var self = this;
			var $form = $(this);
			var o = $.extend(true, {}, $.fn.zupload.defaults, $form.zoptions("zupload"), options);
			var $files = o.find.call(self, o, INPUT);
			// 没有文件, 或者 不支持FormData(h5特性), 或者 不支持玩兔, 直接提交
			if ($files.length == 0 || !window.FormData || !window.Wantu) {
				$.isFunction(o.done) && o.done.call(self, { finish:0, count:$files.length });
				return;
			}

			// token
			// 如果是计算好了的BASE64字符串, 则直接使用
			// 如果是url则发送请求从服务器获取授权凭证
			// 如果是json { ak,sk,namespace,... }, 则调用HmacSHA1计算token
			var url;
			if ($.dt.isString(o.token)) {
				if (/^https?:\/\/.+/.test(o.token) || o.token.startsWith('/')) {
					url = o.token;
				}
			} else if ($.isPlainObject(o.token)) {
				if ("GET" in o.token || "POST" in o.token) {
					url = o.token;
				} else if ("ak" in o.token && "sk" in o.token && "namespace" in o.token) {
					// 计算token
					var ak = o.token.ak; delete o.token.ak;
					var sk = o.token.sk; delete o.token.sk;
					if (!o.token.expiration) { o.token.expiration = -1; }
	
					// 计算规则见REST API 2.3 上传凭证  计算方法
					// http://baichuan.taobao.com/docs/doc.htm?treeId=38&articleId=102763&docType=1
					var C = CryptoJS;
	                var policy = C.enc.Base64.stringify(C.enc.Utf8.parse(JSON.stringify(o.token)));
	                var signed = C.HmacSHA1(policy, C.enc.Utf8.parse(sk));
	                o.token = "UPLOAD_AK_TOP " + C.enc.Base64.stringify(C.enc.Utf8.parse(ak + ":" + policy + ":" + signed));
				} else {
					throw Error("token format error");
				}
			} else {
				throw Error("token format error");
			}

			if (!url) {
				upload.call(self, $files, o);
			} else {
				// 通过服务器获取授权信息
				$.zajax(url, function(json) { // json = { body:{ sizeLimit:int, token:base64string } }
					// 取服务器返回的token
					o.token = $.zhh.field(json, o.tokenField);
					// options没有配置sizeLimit则取服务器返回的sizeLimit
					if (o.sizeLimit == undefined || o.sizeLimit <= 0) { o.sizeLimit = $.zhh.field(json, o.sizeLimitField); }
					upload.call(self, $files, o);
				});
			}
		});
	};
	$.fn.zupload.defaults = {
		tokenField: "body.token", // 通过服务器获取授权信息后如果获取token对象
		sizeLimitField: "body.sizeLimit", // 通过服务器获取授权信息后如何获取sizeLimit
		find : function(o, key) {
			return $(this).find("input[type=file],img").filter(function() {
				var input = $(this).attr(key);
				var matches = input != undefined && input != "false";
				if (matches && this.type == "file") {
					matches = this.files && this.files.length > 0;
				}
				return matches;
			});
		},
		errorCode: {
			FileIsEmpty:"未选择文件",
			BadRequest:'非法请求', //	400	请求数据格式不对，联系小二
			InvalidArgument:'参数错误', //	400	根据错误信息检查参数
			Unauthorized:'授权失败', //	401	检查key和secret是否正确
			ResourceNotFound:'文件或目录不存在', //	404	控制台检查文件或目录是否存在
			InternalError:'服务端内部错误', //	500	联系小二 
			Timeout:'请求超时，服务端超时', // 	504	重试或者联系小二
			MediaTypeError:'文件类型校验失败', //	400	检查文件的类型和上传策略设置的类型是否一致
			SignError:'MD5校验出错', //	400	（文件内容被修改） 重新生成MD5，重新上传
			NameDuplicated:'文件名称或目录名称重复', //	400	控制台检查文件或目录是否存在, 换一个名称或使用覆盖上传
			LimitExceeded:'文件大小超过限制', //	400	单次上传文件大小不能超过10M  也不能超过上传策略中大小限制
			InvalidToken:'Token非法或过期', //	400	重新生成token
			InvalidSid:'SID非法', //	400	重新上传
			InvalidUploadPolicy:'上传策略非法', //	400	检查上传策略的各属性是否正确
			InvalidNamespace:'Namespace非法', //	400	检查namespace是否正确，是否是控制台里的namespace
			ContextExpired:'分片上传，上下文失效、过期', //	400	从第一片开始重新上传
			NonEmpty:'非空目录不能删除', //	400	先删除子目录或文件
			CallbackError:'上传成功，但回调失败', // （包括业务服务器异常、tae服务器异常以及tae服务器间的网络异常）	200	检查回调设置和对应url是否可用
			RequestThrottled:'服务被限流', //	429	降低访问频率
			NotAllowedFileType:'不允许的文件类型', //一般是因为用户被限制	403	联系小二
			NotAllowed:'不允许的调用', //有可能因为用户非白名单	403	联系小二
			ServiceStop:'服务被暂停' //	400	看是否有欠费，若无欠费记录，联系小二
		}
	};
	var upload = function($files, o) {
		var form = this;
		var $form = $(this);
		// 上传文件
		$files.each(function(i) {
			var file = this;
			var $file = $(this);
			var e = { element:file, index:i, count:$files.length };
			// 未选中文件
			if (this.files && this.files.length == 0) {
				/*
				// 未选择文件则跳过
				$form.queue(KEY, function() {
					handleError.call(form, o, $.extend({}, e, {result:{ code:'FileIsEmpty' }}));
				});
				*/
				return true;
			}
			// Wantu.upload()上传回调函数
			var callback = function(percent, result) { // result = string|json{code,message}
				if (percent < 0) { // 上传失败
					handleError.call(form, o, $.extend({}, e, {result:result}));
					return;
				}

				if (percent < 100) {
					// 汇报进度
					$.isFunction(o.process) && o.process.call(form, $.extend({}, e, {percent:percent}));
				} else if (result) { // 上传完成
					// 分包上传时, 有可能多次返回100%, 但只有最后一次有result
					$.isFunction(o.process) && o.process.call(form, $.extend({}, e, {percent:100}));
					// 将文件URL填充到input中
					var $input = findOrCreateInput($form, $file);
					$input.val(result.url);
					$.isFunction(o.step) && o.step.call(form, $.extend({}, e, {url:result.url}));
					// 继续上传
					$form.dequeue(KEY);
				}
			};
			$form.queue(KEY, function() {
				var content;
				if ($file.is("img")) {
					// 将图片转换为blob, 加上type, 伪装成file对象
					content = imgToFile(file);
				} else {
					content = file.files[0];
				}
				// 开始上传
				Wantu.upload($.extend(true, {}, o, { file:content,  callback: callback }));
			});
		});
		// 全部完成后回调
		$form.promise(KEY).done(function() {
			$.isFunction(o.done) && o.done.call(self, { finish:$files.length, count:$files.length });
		});
		// 开始执行
		$form.dequeue(KEY);
	};
	var handleError = function(o, e) {
		var dequeue = true;
		if ($.isFunction(o.error)) {
			// e.result = string|json{code,message}
			if ($.isPlainObject(e.result)) {
				e.message = o.errorCode[e.result.code] || e.result.message;
			} else {
				e.message = e.result;
			}
			// return true则失败后仍然继续上传
			dequeue = o.error.call(this, e);
		}
		if (dequeue === true) { $(this).dequeue(KEY); }
	};
	var findOrCreateInput = function($form, $file) {
		var input = $file.attr(INPUT);
		// data-zupload="true", 只需上传, 不需要保存到input中
		// data-zupload="false", 在一开始就过滤掉了, 不会走到这里
		if (input == "true") { return $([]); }
		var $input;
		var i = $form.find("["+INPUT+"='"+ input +"']").index($file[0]);
		if (/\[i\]/.test(input)) { // data-zupload="image[i]"
			input = input.replace(/\[i\]/g, "["+i+"]"); // 把i替换成具体的序号
			$input = $form.find("input[name='"+ input +"']");
		} else { // data-zupload="image"
			$input = $form.find("input[name='"+ input +"']:eq("+i+")");
		}
		if ($input.length == 0) {
			$input = $('<input type="hidden" name="'+ input +'">').insertAfter($file);
		}
		return $input;
	};
	var imgToFile = function(img) {
		var base64Data = /^data:([\-\w]*\/[\-\w]*)(;charset=[\-\w]*)?(;base64)?,/i;
		var data;
		var mimeType = "image/png";
		var fileName = "image.png";
		if (base64Data.exec(img.src)) {
			data = img.src;
		} else {
			fileName = img.src.replace(/[\?#].*/, "");
			var index = fileName.lastIndexOf("/");
			if (index >= 0) { fileName = fileName.substring(index + 1); }
			var m = /\.(\w+)$/.exec(fileName);
			if (m) { mimeType = "image/" + m[1].replace(/jpg/, "jpeg"); }
			// 将图片画到画布上, 再转换为DataURL
			var canvas = document.createElement('canvas');
			canvas.width = img.naturalWidth;
			canvas.height = img.naturalHeight;
			canvas.style.display = "none";
			document.body.appendChild(canvas);
			canvas.getContext('2d').drawImage(img, 0, 0);

			// 这里, 会存在跨域的问题
			// 需要服务器允许且在img上加上crossOrigin才行, <img crossOrigin="*" src="..."/>
			data = canvas.toDataURL();
		}

		// DataURL转换为Blob
		// data = "data:image/png;base64,..."
		var bytes = window.atob(data.replace(base64Data, ''));
		var array = new Uint8Array(bytes.length);
		for (var i = 0; i < bytes.length; i++ ) {
			array[i] = bytes.charCodeAt(i);
		}

		var matcher = base64Data.exec(data);
		if (matcher) { 
			mimeType = matcher[1]; // 如mimeType=image/png
			fileName = fileName.replace(/\.[\-\w]+$/, "") + mimeType.replace(/(image\/)/, ".");
		}
		// 将图片转换为blob, 加上type和name, 伪装成file对象
		var blob = new Blob([array], { type:mimeType });
		blob.name = fileName;
		return blob;
	};
}(jQuery);

/**
 * -------------------------------------------------------------
 * 阿里玩兔图片上传
 * 支持自定义占位符
 * {yy} {yyyy} {MM} {dd} {HH} {mm} {ss} {SSS} {random-1}~{random-10}
 * -------------------------------------------------------------
	Wantu.upload({
		file: file,   // 文件, 必填, html5 file类型, 不需要读数据流
		token: xxx,  // token, 必填, 由服务器生成
		callback: function (percent, result) {
			// percent(上传百分比): -1失败; 0-100上传的百分比; 100即完成上传
			// result(服务端返回的responseText, json格式)
		}
	});
 * -------------------------------------------------------------
 * @author 赵卉华
 * date: 2016-05-11
 * -------------------------------------------------------------
 */
+function() {
	'use strict';
	var timeParts = ["yy", "yyyy", "MM", "dd", "HH", "mm", "ss", "SSS"];
	var fillVerParams = function(formData) {
		var date = new Date();
		// 生成参数, 作为自定义占位符
		// 日期时间
		for ( var i = 0; i < timeParts.length; i ++) {
			formData.append("var-" + timeParts[i], Dates.format(date, timeParts[i]));
		}
		// 随机数1~10位
		var size = 10;
		var number = Randoms.number(size);
		for (var i = 1; i < size; i++) {
			formData.append("var-random-" + i, number.substring(0, i));
		}
		formData.append("var-random-" + size, number);
	};

	// 以下代码copy自阿里玩兔JSSDK示例
	// http://baichuan.taobao.com/docs/doc.htm?treeId=38&articleId=103547&docType=1
	function uploadJSSDK(config) {

		var file = config.file,
			// dir = config.dir || "",
			token = config.token,
			callback = config.callback || function () {},
			retries = config.retries || 0,
			sizeLimit = config.sizeLimit || 0,
			// configName = config.name||file.name,
			upload_url = "http://upload.media.aliyun.com/api/proxy/upload",
			blockInit = "http://upload.media.aliyun.com/api/proxy/blockInit",
			blockUpload = "http://upload.media.aliyun.com/api/proxy/blockUpload",
			blockComplete = "http://upload.media.aliyun.com/api/proxy/blockComplete",
			chunkSize = config.chunkSize||4 * 1024 * 1024,
			offset = 0,
			blob = file,
			id,  //上传唯一id，上传初始化请求获得
			uploadId, //分片上传id，上传初始化请求获得
			tags = [],//每次分片上传得到的md5
			curChunkSize = [], chunkBlob = [];  //分块上传的各个块

		function handleError(result, finish, chunk, status) {
			//重试
			if (retries-- > 0 && !finish && status != 599) {
				if (file.size > chunkSize) {
					//分块上传
					uploadNextChunk(chunk, curChunkSize[chunk], chunkBlob[chunk]);
				} else {
					//普通上传
					uploadSingle();
				}
			} else {
				callback(-1, result);
			}
		}


		//上传分块之前需要提交个start请求，上传结束需要提交个finish请求
		function startChunks() {
			var tmp = 0, size;
			while (tmp < blob.size) {
				size = Math.min(chunkSize, blob.size - tmp);
				curChunkSize.push(size);
				chunkBlob.push(blob.slice(tmp, tmp + size));
				tmp += size;
			}
			uploadNextChunk(0, curChunkSize[0], chunkBlob[0])
		}

		function uploadNextChunk(chunk, curchunksize, chunkblob) {
			var formData = new FormData();
			formData.append('size', curchunksize);
			var url;
			if (chunk == 0) {
				// dir和name由UploadPolicy决定
				// formData.append('dir', dir);
				// formData.append('name', configName);
				fillVerParams(formData);
				url = blockInit;
			} else {
				formData.append('id', id);
				formData.append('uploadId', uploadId);
				formData.append('partNumber', chunk + 1); //chunk从0开始,partNumber从1开始
				url = blockUpload;
			}
			formData.append('content', chunkblob, blob.name);

			ajax(url, formData, function (e) {
				var status = e.status,
					message = e.statusText || "";
				if (status == 200 && message == "OK") {
					var result = JSON.parse(e.responseText);
					tags[chunk] = result.eTag;
					offset += curchunksize;

					var percent = Math.ceil(offset / file.size * 100);
					//改变显示的中间状态
					callback(percent);

					if (chunk == 0) {
						id = result.id;
						uploadId = result.uploadId;
						for (var i = 1; i < chunkBlob.length; i++) {
							uploadNextChunk(i, curChunkSize[i], chunkBlob[i])
						}
					}

					// Check if file is uploaded
					if (offset >= blob.size) {
						blob = null;
						chunkblob = formData = null; // Free memory
						finishChunks();
					}
				} else {
					handleError(JSON.parse(e.responseText), 0, chunk, status);
				}

			});
		}

		function finishChunks() {
			var parts = [];
			for (var i = 0; i < tags.length; i++) {
				parts[i] = {
					"partNumber": i + 1,
					"eTag": tags[i]
				}
			}
			parts = btoa(JSON.stringify(parts));
			var formData = new FormData();
			formData.append('id', id);
			formData.append('uploadId', uploadId);
			formData.append('parts', parts);

			ajax(blockComplete, formData, function (e) {
				var status = e.status,
					message = e.statusText || "";
				if (status == 200 && message == "OK") {
					callback(100, JSON.parse(e.responseText));
				} else {
					handleError(JSON.parse(e.responseText), 1, "", status);  //分片上传已经完成就不能重试了
				}
			})
		}

		function uploadSingle() {
			var formData = new FormData();
			fillVerParams(formData);
			// dir和name由UploadPolicy决定
			// formData.append('dir', dir);
			// formData.append('name', configName);
			formData.append('size', file.size);
			formData.append('content', file);
			var url = upload_url;

			ajax(url, formData, function (e) {
				var status = e.status,
					message = e.statusText || "";
				if (status == 200 && message == "OK") {
					callback(100, JSON.parse(e.responseText));
				} else {
					handleError(JSON.parse(e.responseText), "", "", status);
				}
			});
		}

		function ajax(url,data,complete){
			var request = new XMLHttpRequest();
			request.open('POST', url);
			request.setRequestHeader('Authorization', token);
			request.setRequestHeader('UserAgent', 'ALIMEDIASDK_JSSDK');
			request.onload = function(e) {
				complete(request);
			};
			request.send(data);
		}

		if (!file || !token) {
			callback(-1, { code:'InvalidArgument' });
			return;
		}

		if (sizeLimit && file.size > sizeLimit) {
			callback(-1, { code:'LimitExceeded' });
			return;
		}

		if (file.size > chunkSize) {
			//分块上传
			startChunks();
		} else {
			//普通上传
			uploadSingle();
		}
	}

	this.Wantu = { upload: uploadJSSDK };

}.call(this);
